class MyBlockingQueue {
    // 此处这里的最大长度, 也可以指定构造方法, 由构造方法的参数来制定.
    private String[] data = new String[1000];
    // 队列的起始位置.
    private volatile int head = 0;
    // 队列的结束位置的下一个位置.
    private volatile int tail = 0;
    // 队列中有效元素的个数.
    private volatile int size = 0;

    // private final Object locker = new Object();

    // 提供核心方法, 入队列和出队列.
    public void put(String elem) throws InterruptedException {
        synchronized (this) {
            while (size == data.length) {
                // 队列满了.
                // 如果是队列满, 继续插入元素, 就会阻塞.
                this.wait();
            }
            // 队列没满, 真正的往里面添加元素
            data[tail] = elem;
            tail++;
            // 如果 tail 自增之后, 到达了数组末尾. 这个时候就需要让它回到开头 (环形队列)
            if (tail == data.length) {
                tail = 0;
            }
            size++;
            // 这个 notify 用来唤醒 take 中的 wait
            this.notify();
        }
    }

    public String take() throws InterruptedException {
        synchronized (this) {
            while (size == 0) {
                // 队列空了.
                this.wait();
            }
            // 队列不空, 就可以把队首元素 (head 位置的元素) 删除掉, 并进行返回.
            String ret = data[head];
            head++;
            if (head == data.length) {
                head = 0;
            }
            size--;
            // 这个 notify 用来唤醒 put 中的 wait
            this.notify();
            return ret;
        }
    }
}

public class Demo24 {
    public static void main(String[] args) {
        // 生产者, 消费者, 分别使用一个线程表示. (也可以使用多个线程)

        MyBlockingQueue queue = new MyBlockingQueue();

        // 消费者
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    String result = queue.take();
                    System.out.println("消费元素: " + result);

                    // 暂时先不 sleep
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 生产者
        Thread t2 = new Thread(() -> {
            int num = 1;
            while (true) {
                try {
                    queue.put(num + "");
                    System.out.println("生产元素: " + num);
                    num++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}